package com.seckill.order.lock;

import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 锁续期抽取
 */
@Slf4j
@AllArgsConstructor//
public class LockRenewAsync implements Runnable{

    private StringRedisTemplate stringRedisTemplate;
    private String key;
    private Long timeout;//释放时间(分布式锁过期时间)
    private TimeUnit timeUnit;
    private CountDownLatch countDownLatch;

    @Override
    public void run() {
        String redis_threadId = stringRedisTemplate.opsForValue().get(key);
        log.info("锁续期开启, 线程id为: {}", Thread.currentThread().getId());
        long count = 0L;
        while (!Thread.currentThread().isInterrupted()){//没有中断,这个续期就正常周期续期
            try {
                //stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
                if (ObjectUtil.isEmpty(redis_threadId)) {//如果分布式锁都没有,就没有必要续期
                    return;
                }
                //不能放到while外面,因为如果续期后,此键值对的有效期因为固定,可能会导致过期,到释放锁的时候要用的时候就没了,所以只能每次都再重新设置一下这个键值对
                //以处理下单的线程唯一标识为键,续期线程id为值,缓存续期线程id,以便后续释放锁的时候可以将续期线程释放掉(interrupt)
                stringRedisTemplate.opsForValue().set(redis_threadId, Thread.currentThread().getId() + "", timeout, TimeUnit.SECONDS);
                if (countDownLatch.getCount() == 0) {
                    return;
                }
                countDownLatch.countDown();
                Thread.sleep(timeUnit.toMillis(timeout) / 3);//每20秒续期//一般是过期时间的三分之一
                stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
                log.info("成功续期, 当前续期次数为: {}, 线程id为: {}",++count, Thread.currentThread().getId());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                //throw new RuntimeException(e);
            }
        }
    }
}
